package com.tianji.learning.service.impl;

import cn.hutool.core.util.ObjectUtil;
import com.tianji.common.autoconfigure.mq.RabbitMqHelper;
import com.tianji.common.exceptions.BizIllegalException;
import com.tianji.common.utils.BooleanUtils;
import com.tianji.common.utils.DateUtils;
import com.tianji.common.utils.UserContext;
import com.tianji.learning.constans.SignRecordConstans;
import com.tianji.learning.domain.vo.SignResultVO;
import com.tianji.learning.mq.messsge.SignMessage;
import com.tianji.learning.service.SignRecordService;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.data.redis.connection.BitFieldSubCommands;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;

import java.time.LocalDate;
import java.time.LocalDateTime;
import java.util.Collections;
import java.util.List;

import static com.tianji.common.constants.MqConstants.Exchange.LEARNING_EXCHANGE;
import static com.tianji.common.constants.MqConstants.Key.SIGN_IN;

@Slf4j
@Service
@RequiredArgsConstructor
public class SignRecordServiceImpl implements SignRecordService {
    private final StringRedisTemplate redisTemplate;
    private final RabbitMqHelper mqHelper;

    /**
     * 签到
     *
     * @return
     */
    @Override
    public SignResultVO addSignRecord() {
        //1.签到
        //1.2获取userId
        Long userId = UserContext.getUser();
        //1.3获取当前时间
        LocalDate now = LocalDate.now();
        //1.3.1指定时间格式,signDay签到的天数
        String format = now.format(DateUtils.DATE_SUFFIX_FORMATTER);
        //1.3.2offest — 1 ，天数-1
        int offest = now.getDayOfMonth() - 1;
        //1.4组装key
        String key = SignRecordConstans.Sign_RECORD_KEY_PRIFIX + userId + format;
        //(bitfield siqn:uid:2:202408 get u8 0)
        //1.5签到（核心）
        //1.5.1 给第一次签到设置过期时间
        boolean flag = false;
        Long keyExsist = redisTemplate.countExistingKeys(Collections.singleton(key));
        if (keyExsist > 0) {
            flag = true;
        }
        Boolean exsit = redisTemplate.opsForValue().setBit(key, offest, true);//签到成功返回的是0，重复签到返回1
        //第一次签到设置过期时间
        if (!flag) {
            //1.5.2设置过期时间
            LocalDateTime beginTime = LocalDateTime.now();
            //1.5.3拿到这个月的最后一天
            LocalDateTime endTime = DateUtils.getMonthBeginTime(LocalDate.now().plusMonths(1));
            redisTemplate.expire(key, DateUtils.between(beginTime, endTime));
        }
        //2.判断签到的结果
        if (BooleanUtils.isTrue(exsit)) {
            throw new BizIllegalException("当天已经签过到，不能重复签到");
        }
        //3.查询连续签到的天数
        int signDayCount = queryConnectSignDay(key, offest);
        //计算连续签到分数
        int connetSroce = connectSginSroce(signDayCount);
        //4.分装vo
        SignResultVO resultVO = new SignResultVO();
        resultVO.setSignDays(signDayCount);
        resultVO.setRewardPoints(connetSroce);

        try {
            //发送消息，记录签到记录
            mqHelper.send(LEARNING_EXCHANGE, SIGN_IN, SignMessage.of(UserContext.getUser(), resultVO.totalPoints()));
        } catch (Exception e) {
            log.error("签到消息发送失败");
        }
        //5.返回
        return resultVO;
    }

    /**
     * 计算连续签到分数
     *
     * @param signDayCount
     * @return
     */
    private int connectSginSroce(int signDayCount) {
        switch (signDayCount) {
            case 7:
                return 10;
            case 14:
                return 20;
            case 28:
                return 40;
            default:
                return 0;
        }
    }

    /**
     * 查询签到天数
     *
     * @return
     */
    @Override
    public Byte[] querySignDayCount() {
        //1.获取userId
        Long userId = UserContext.getUser();
        //2.获取当前时间
        LocalDate now = LocalDate.now();
        //3.指定时间格式,signDay签到的天数
        String format = now.format(DateUtils.DATE_SUFFIX_FORMATTER);
        //4.offest — 1 ，天数-1
        int offest = now.getDayOfMonth() - 1;
        //5.组装key
        String key = SignRecordConstans.Sign_RECORD_KEY_PRIFIX + userId + format;
        List<Long> listConnectDay = redisTemplate.opsForValue().bitField(key,
                BitFieldSubCommands.create().get(BitFieldSubCommands.BitFieldType.unsigned(offest)).valueAt(0));
        if (ObjectUtil.isEmpty(listConnectDay)) {
            return new Byte[]{};
        }
        //6.拿到返回的这个月的签到天数
        Long signDay = listConnectDay.get(0);
        //6.1创建一个字节数组来装签到的天数
        Byte[] bytes = new Byte[offest];
        //6.2倒着放元素
        int pro = bytes.length - 1;
        while (pro >= 0) {
            //6.3做完与运算后，倒着放元素
            bytes[pro--] = (byte) (signDay & 1);
            //6.4签到天数右移一位
            signDay >>>= 1;
        }
        return bytes;
    }

    /**
     * 查询连续签到的天数
     *
     * @param key
     * @param offest
     * @return
     */
    private int queryConnectSignDay(String key, int offest) {
        List<Long> listConnectDay = redisTemplate.opsForValue().bitField(key,
                BitFieldSubCommands.create().get(BitFieldSubCommands.BitFieldType.unsigned(offest)).valueAt(0));
        if (ObjectUtil.isEmpty(listConnectDay)) {
            return 0;
        }
        int signRecords = listConnectDay.get(0).intValue();
        int day = 0;
        while ((signRecords & 1) == 1) {
            day++;
            signRecords >>>= 1;
        }
        return day;
    }
}


